package com.lanyan.web.controller;


import com.google.gson.annotations.SerializedName;
import com.lanyan.common.utils.WXPayUtility;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 发起转账
 */
@Component
@Slf4j
public class TransferToUser {
    private static String HOST = "https://api.mch.weixin.qq.com";
    private static String METHOD = "POST";
    private static String PATH = "/v3/fund-app/mch-transfer/transfer-bills";

    private static String mchId = "1700102694";
    private static String certificateSerialNoCode = "29E6350984DB4441D2E5976E54CBF333D770E65B";

    private static String privateKeyPath = "/www/wwwroot/l2848/cert/1700102694_20251001_cert/apiclient_key.pem";
    //private static String privateKeyPath = "D:/wx/WXCertUtil/cert/1700102694_20251001_cert/apiclient_key.pem";

    private static String wechatPayPublicKeyIdCode= "PUB_KEY_ID_0117001026942024120300357400000002";

    //private static String wechatPayPublicKeyPath = "C:/Users/xiaoli/Desktop/新建文件夹/pub_key.pem";
    private static String wechatPayPublicKeyPath = "/www/wwwroot/l2848/cert/pubKey/pub_key.pem";

    private static String appId = "wx563af9a9435a349a";

    private static String transferSceneId = "1000";

    // 添加无参构造函数
    public TransferToUser() {
        // 使用静态变量初始化实例变量
        // TODO: 请准备商户开发必要参数，参考：https://pay.weixin.qq.com/doc/v3/merchant/4013070756
        this.mchid = mchId; // 商户号，是由微信支付系统生成并分配给每个商户的唯一标识符，商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
        this.certificateSerialNo = certificateSerialNoCode; // 商户API证书序列号，如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
        this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyPath); // 商户API证书私钥文件路径，本地文件路径
        this.wechatPayPublicKeyId = wechatPayPublicKeyIdCode; // 微信支付公钥ID，如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
        this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyPath); // 微信支付公钥文件路径，本地文件路径
    }


    public Map<String, String> transferToUser(String outBillNo, String openid, Long transferAmount, String transferRemark) {

        TransferToUserRequest request = new TransferToUserRequest();
        request.appid = appId;
        //out_bill_no（商户单号）是商户系统内部生成的唯一标识，每次发起转账时，商户需确保该单号在自身系统内唯一。因此，每次发起转账时，out_bill_no 都会不同，不能重复使用。若需重新转账，应生成新的商户单号。
        request.outBillNo = outBillNo;
        request.transferSceneId = transferSceneId;
        //用户在商户appid下的唯一标识。发起转账前需获取到用户的OpenID
        request.openid = openid;
        //request.userName = "liyikuo";
        request.transferAmount = transferAmount;
        request.transferRemark = transferRemark;
        request.transferSceneReportInfos = new ArrayList<>();
        {
            TransferSceneReportInfo transferSceneReportInfosItem0 = new TransferSceneReportInfo();
            transferSceneReportInfosItem0.infoType = "活动名称";
            transferSceneReportInfosItem0.infoContent = "新会员有礼";
            request.transferSceneReportInfos.add(transferSceneReportInfosItem0);
            TransferSceneReportInfo transferSceneReportInfosItem1 = new TransferSceneReportInfo();
            transferSceneReportInfosItem1.infoType = "奖励说明";
            transferSceneReportInfosItem1.infoContent = "注册会员抽奖一等奖";
            request.transferSceneReportInfos.add(transferSceneReportInfosItem1);
        }
        ;

        Map<String, String> resultMap = new HashMap<>();
        try {
            TransferToUserResponse response = this.run(request);
            log.info("发起转账结果：{}", WXPayUtility.toJson(response));
            System.out.println(WXPayUtility.toJson(response));
            TransferBillStatus state = response.state;
            String packageInfo = response.packageInfo;
            switch (state) {
                case ACCEPTED:
                    //转账已受理，可原单重试（非终态）
                    resultMap.put("ACCEPTED", packageInfo);
                    return resultMap;
                case PROCESSING:
                    //转账锁定资金中。如果一直停留在该状态，建议检查账户余额是否足够，如余额不足，可充值后再原单重试（非终态）。
                    resultMap.put("PROCESSING", packageInfo);
                    return resultMap;
                case WAIT_USER_CONFIRM:
                    // 待收款用户确认，当前转账单据资金已锁定，可拉起微信收款确认页面进行收款确认（非终态）。
                    resultMap.put("WAIT_USER_CONFIRM", packageInfo);
                    return resultMap;
                case TRANSFERING:
                    //转账中，可拉起微信收款确认页面再次重试确认收款（非终态）。
                    resultMap.put("TRANSFERING", packageInfo);
                    return resultMap;
                case SUCCESS:
                    //转账成功，表示转账单据已成功（终态）。
                    resultMap.put("SUCCESS", packageInfo);
                    return resultMap;
                case FAIL:
                    //转账失败，表示该笔转账单据已失败。若需重新向用户转账，请重新生成单据并再次发起（终态）。
                    resultMap.put("FAIL", packageInfo);
                    return resultMap;
                case CANCELING:
                    //转账撤销中，商户撤销请求受理成功，该笔转账正在撤销中，需查单确认撤销的转账单据状态（非终态）。
                    resultMap.put("CANCELING", packageInfo);
                    return resultMap;
                case CANCELLED:
                    //转账撤销完成，代表转账单据已撤销成功（终态）。
                    resultMap.put("CANCELLED", packageInfo);
                    return resultMap;
                default:
                    //其他状态，可原单重试（非终态）。
                    resultMap.put("default", packageInfo);
            }
        } catch (WXPayUtility.ApiException e) {
            log.error("商家转账 请求失败，错误码：" + e.getErrorCode() + "，错误信息：" + e.getErrorMessage());
            //请求失败，根据状态码执行不同的逻辑
            e.printStackTrace();
            resultMap.put("err", "提现失败，系统错误!");
            return resultMap;
        }
        resultMap.put("err", "提现失败，系统错误!");
        return resultMap;
    }

    public TransferToUserResponse run(TransferToUserRequest request) {
        String uri = PATH;
        String reqBody = WXPayUtility.toJson(request);

        Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
        reqBuilder.addHeader("Accept", "application/json");
        reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, reqBody));
        reqBuilder.addHeader("Content-Type", "application/json");
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
        reqBuilder.method(METHOD, requestBody);
        Request httpRequest = reqBuilder.build();

        // 发送HTTP请求
        OkHttpClient client = new OkHttpClient.Builder().build();
        try (Response httpResponse = client.newCall(httpRequest).execute()) {
            String respBody = WXPayUtility.extractBody(httpResponse);
            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
                // 2XX 成功，验证应答签名
                WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
                        httpResponse.headers(), respBody);
                // 从HTTP应答报文构建返回数据
                return WXPayUtility.fromJson(respBody, TransferToUserResponse.class);
            } else {
                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
            }
        } catch (IOException e) {
            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
        }
    }

    private final String mchid;
    private final String certificateSerialNo;
    private final PrivateKey privateKey;
    private final String wechatPayPublicKeyId;
    private final PublicKey wechatPayPublicKey;

    // 移除原来的构造函数或者将其改为私有
    private TransferToUser(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
        this.mchid = mchid;
        this.certificateSerialNo = certificateSerialNo;
        this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
        this.wechatPayPublicKeyId = wechatPayPublicKeyId;
        this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
    }

    public String encrypt(String plainText) {
        return WXPayUtility.encrypt(this.wechatPayPublicKey, plainText);
    }

    public static class TransferToUserRequest {
        @SerializedName("appid")
        public String appid;

        @SerializedName("out_bill_no")
        public String outBillNo;

        @SerializedName("transfer_scene_id")
        public String transferSceneId;

        @SerializedName("openid")
        public String openid;

/*        @SerializedName("user_name")
        public String userName;*/

        @SerializedName("transfer_amount")
        public Long transferAmount;

        @SerializedName("transfer_remark")
        public String transferRemark;

        @SerializedName("notify_url")
        public String notifyUrl;

        @SerializedName("user_recv_perception")
        public String userRecvPerception;

        @SerializedName("transfer_scene_report_infos")
        public List<TransferSceneReportInfo> transferSceneReportInfos = new ArrayList<TransferSceneReportInfo>();
    }

    public static class TransferToUserResponse {
        @SerializedName("out_bill_no")
        public String outBillNo;

        @SerializedName("transfer_bill_no")
        public String transferBillNo;

        @SerializedName("create_time")
        public String createTime;

        @SerializedName("state")
        public TransferBillStatus state;

        @SerializedName("package_info")
        public String packageInfo;
    }

    public static class TransferSceneReportInfo {
        @SerializedName("info_type")
        public String infoType;

        @SerializedName("info_content")
        public String infoContent;
    }

    public enum TransferBillStatus {
        @SerializedName("ACCEPTED")
        ACCEPTED,
        @SerializedName("PROCESSING")
        PROCESSING,
        @SerializedName("WAIT_USER_CONFIRM")
        WAIT_USER_CONFIRM,
        @SerializedName("TRANSFERING")
        TRANSFERING,
        @SerializedName("SUCCESS")
        SUCCESS,
        @SerializedName("FAIL")
        FAIL,
        @SerializedName("CANCELING")
        CANCELING,
        @SerializedName("CANCELLED")
        CANCELLED
    }

}